/* src/interfaces/ecpg/pgtypeslib/dt.h */

#ifndef DT_H
#define DT_H

#include <pgtypes_timestamp.h>

#include <time.h>

#define MAXTZLEN			 10

typedef int32 fsec_t;

#define USE_POSTGRES_DATES				0
#define USE_ISO_DATES					1
#define USE_SQL_DATES					2
#define USE_GERMAN_DATES				3

#define INTSTYLE_POSTGRES			  0
#define INTSTYLE_POSTGRES_VERBOSE	  1
#define INTSTYLE_SQL_STANDARD		  2
#define INTSTYLE_ISO_8601			  3

#define INTERVAL_FULL_RANGE (0x7FFF)
#define INTERVAL_MASK(b) (1 << (b))
#define MAX_INTERVAL_PRECISION 6

#define DTERR_BAD_FORMAT		(-1)
#define DTERR_FIELD_OVERFLOW	(-2)
#define DTERR_MD_FIELD_OVERFLOW (-3)	/* 触发关于日期样式的提示 */
#define DTERR_INTERVAL_OVERFLOW (-4)
#define DTERR_TZDISP_OVERFLOW	(-5)


#define DAGO			"ago"
#define DCURRENT		"current"
#define EPOCH			"epoch"
#define INVALID			"invalid"
#define EARLY			"-infinity"
#define LATE			"infinity"
#define NOW				"now"
#define TODAY			"today"
#define TOMORROW		"tomorrow"
#define YESTERDAY		"yesterday"
#define ZULU			"zulu"

#define DMICROSEC		"usecond"
#define DMILLISEC		"msecond"
#define DSECOND			"second"
#define DMINUTE			"minute"
#define DHOUR			"hour"
#define DDAY			"day"
#define DWEEK			"week"
#define DMONTH			"month"
#define DQUARTER		"quarter"
#define DYEAR			"year"
#define DDECADE			"decade"
#define DCENTURY		"century"
#define DMILLENNIUM		"millennium"
#define DA_D			"ad"
#define DB_C			"bc"
#define DTIMEZONE		"timezone"

/*
 * 解析的基本时间字段定义。
 *
 *	上午或下午：am, pm，或24小时制。
 *	千年：公元，公元前
 */

#define AM		0
#define PM		1
#define HR24	2

#define AD		0
#define BC		1

/*
 * 时间解码的字段类型。
 *
 * 这些字段的数量不能超过无符号整数中的位数
 * 因为它们在解析和解码过程中会变成位掩码。
 *
 * 此外，YEAR、MONTH、DAY、HOUR、MINUTE、SECOND的值
 * 必须在0..14的范围内，以便相关的位掩码能够适应
 * INTERVAL的typmod值的左半部分。
 *
 * 从src/include/utils/datetime.h中复制并粘贴了这些值
 * 2008-11-20，改变了其中的一些值。
 */

#define RESERV	0
#define MONTH	1
#define YEAR	2
#define DAY		3
#define JULIAN	4
#define TZ		5				/* 固定偏移时区缩写 */
#define DTZ		6				/* 固定偏移时区缩写，夏令时 */
#define DYNTZ	7				/* 动态时区缩写（未实现） */
#define IGNORE_DTF	8
#define AMPM	9
#define HOUR	10
#define MINUTE	11
#define SECOND	12
#define MILLISECOND 13
#define MICROSECOND 14
#define DOY		15
#define DOW		16
#define UNITS	17
#define ADBC	18
/* 这些仅适用于相对日期 */
#define AGO		19
#define ABS_BEFORE		20
#define ABS_AFTER		21
/* 通用字段以帮助解析 */
#define ISODATE 22
#define ISOTIME 23
/* 解析两个单词的时区规范的hack "MET DST"等 */
#define DTZMOD	28				/* "DST"作为一个单独的词 */
/* 保留给未识别的字符串值 */
#define UNKNOWN_FIELD	31


/*
 * 用于时间解析和解码的标记字段定义。
 *
 * 一些字段类型代码（见上文）将这些作为datetktbl[]中的“值”。
 * 这些也用于DecodeDateTime及其相关函数的位掩码
 * 因此实际限制它们在[0,31]之内。
 * - thomas 97/06/19
 * 这些字段并不全用于DecodeDateTime的掩码
 * 因此允许一些大于31的值。- thomas 1997-11-17
 *
 * 警告：代码中有未记录的假设，大多数这些
 * 值不等于IGNORE_DTF或RESERV。在重新编号时
 * 一定要非常小心这些看似独立的列表中的值 :-(
 */

#define DTK_NUMBER		0
#define DTK_STRING		1

#define DTK_DATE		2
#define DTK_TIME		3
#define DTK_TZ			4
#define DTK_AGO			5

#define DTK_SPECIAL		6
#define DTK_EARLY		9
#define DTK_LATE		10
#define DTK_EPOCH		11
#define DTK_NOW			12
#define DTK_YESTERDAY	13
#define DTK_TODAY		14
#define DTK_TOMORROW	15
#define DTK_ZULU		16

#define DTK_DELTA		17
#define DTK_SECOND		18
#define DTK_MINUTE		19
#define DTK_HOUR		20
#define DTK_DAY			21
#define DTK_WEEK		22
#define DTK_MONTH		23
#define DTK_QUARTER		24
#define DTK_YEAR		25
#define DTK_DECADE		26
#define DTK_CENTURY		27
#define DTK_MILLENNIUM	28
#define DTK_MILLISEC	29
#define DTK_MICROSEC	30
#define DTK_JULIAN		31

#define DTK_DOW			32
#define DTK_DOY			33
#define DTK_TZ_HOUR		34
#define DTK_TZ_MINUTE	35
#define DTK_ISOYEAR		36
#define DTK_ISODOW		37


/*
 * 时间解析的位掩码定义。
 */
/* 从src/include/utils/datetime.h中复制并粘贴了这些值 */
#define DTK_M(t)		(0x01 << (t))
#define DTK_ALL_SECS_M	   (DTK_M(SECOND) | DTK_M(MILLISECOND) | DTK_M(MICROSECOND))
#define DTK_DATE_M		(DTK_M(YEAR) | DTK_M(MONTH) | DTK_M(DAY))
#define DTK_TIME_M		(DTK_M(HOUR) | DTK_M(MINUTE) | DTK_M(SECOND))

/*
 * 区间、时间戳等输入和输出的工作缓冲区大小。
 * 需要更多工作空间的输入将被提前拒绝。更长的输出
 * 将溢出缓冲区，因此这必须足够满足所有可能的输出。根据
 * 当前的写作，PGTYPESinterval_to_asc()需要最多约90字节的空间。
 */
#define MAXDATELEN		128
/* 日期字符串中可能的最大字段数 */
#define MAXDATEFIELDS	25
/* 仅存储这么多字符在datetktbl中 */
#define TOKMAXLEN		10

/* 保持这个结构小；它使用很多 */
typedef struct
{
	char		token[TOKMAXLEN + 1];	/* 始终以NUL结尾 */
	char		type;			/* 见上面的字段类型代码 */
	int32		value;			/* 含义取决于类型 */
} datetkn;


/* FMODULO()
 * 替换modf()的宏，在某些平台上该函数存在问题。
 * t = 输入和余数
 * q = 整数部分
 * u = 除数
 */
#define FMODULO(t,q,u) \
do { \
	(q) = (((t) < 0) ? ceil((t) / (u)): floor((t) / (u))); \
	if ((q) != 0) (t) -= rint((q) * (u)); \
} while(0)

/* TMODULO()
 * 像FMODULO()，但适用于时间戳数据类型（现在始终为int64）。
 * 我们假设int64遵循C99的除法语义（负商截断为零）。
 */
#define TMODULO(t,q,u) \
do { \
	(q) = ((t) / (u)); \
	if ((q) != 0) (t) -= ((q) * (u)); \
} while(0)

/* 在timestamp.h和ecpg/dt.h中 */
#define DAYS_PER_YEAR	365.25	/* 假设每四年一个闰年 */
#define MONTHS_PER_YEAR 12
/*
 *	每月天数非常不精确。更准确的值是
 *	365.2425/12 = 30.436875，或“30天10:29:06”。现在我们只
 *	返回一个整数的天数，但将来或许我们也应该
 *	返回一个“时间”值以供使用。ISO 8601 建议
 *	30天。
 */
#define DAYS_PER_MONTH	30		/* 假设每月正好30天 */
#define HOURS_PER_DAY	24		/* 假设没有夏令时变化 */

/*
 *	这没有调整不均匀的夏令时间隔或闰
 *	秒，并且粗略估计闰年。每年天数的更准确值
 *	是365.2422。
 */
#define SECS_PER_YEAR	(36525 * 864)	/* 避免浮点计算 */
#define SECS_PER_DAY	86400
#define SECS_PER_HOUR	3600
#define SECS_PER_MINUTE 60
#define MINS_PER_HOUR	60

#define USECS_PER_DAY	INT64CONST(86400000000)
#define USECS_PER_HOUR	INT64CONST(3600000000)
#define USECS_PER_MINUTE INT64CONST(60000000)
#define USECS_PER_SEC	INT64CONST(1000000)

/*
 * 日期/时间验证
 * 包括闰年检查。
 */
#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))

/*
 * Julian 日期支持 --- 见后端的 timestamp.h 中的注释。
 */

#define JULIAN_MINYEAR (-4713)
#define JULIAN_MINMONTH (11)
#define JULIAN_MINDAY (24)
#define JULIAN_MAXYEAR (5874898)
#define JULIAN_MAXMONTH (6)
#define JULIAN_MAXDAY (3)

#define IS_VALID_JULIAN(y,m,d) \
	(((y) > JULIAN_MINYEAR || \
	  ((y) == JULIAN_MINYEAR && ((m) >= JULIAN_MINMONTH))) && \
	 ((y) < JULIAN_MAXYEAR || \
	  ((y) == JULIAN_MAXYEAR && ((m) < JULIAN_MAXMONTH))))

#define MIN_TIMESTAMP	INT64CONST(-211813488000000000)
#define END_TIMESTAMP	INT64CONST(9223371331200000000)

#define IS_VALID_TIMESTAMP(t)  (MIN_TIMESTAMP <= (t) && (t) < END_TIMESTAMP)

#define UTIME_MINYEAR (1901)
#define UTIME_MINMONTH (12)
#define UTIME_MINDAY (14)
#define UTIME_MAXYEAR (2038)
#define UTIME_MAXMONTH (01)
#define UTIME_MAXDAY (18)

#define IS_VALID_UTIME(y,m,d) ((((y) > UTIME_MINYEAR) \
 || (((y) == UTIME_MINYEAR) && (((m) > UTIME_MINMONTH) \
  || (((m) == UTIME_MINMONTH) && ((d) >= UTIME_MINDAY))))) \
 && (((y) < UTIME_MAXYEAR) \
 || (((y) == UTIME_MAXYEAR) && (((m) < UTIME_MAXMONTH) \
  || (((m) == UTIME_MAXMONTH) && ((d) <= UTIME_MAXDAY))))))

#define DT_NOBEGIN		(-INT64CONST(0x7fffffffffffffff) - 1)
#define DT_NOEND		(INT64CONST(0x7fffffffffffffff))

#define TIMESTAMP_NOBEGIN(j)	do {(j) = DT_NOBEGIN;} while (0)
#define TIMESTAMP_NOEND(j)			do {(j) = DT_NOEND;} while (0)
#define TIMESTAMP_IS_NOBEGIN(j) ((j) == DT_NOBEGIN)
#define TIMESTAMP_IS_NOEND(j)	((j) == DT_NOEND)
#define TIMESTAMP_NOT_FINITE(j) (TIMESTAMP_IS_NOBEGIN(j) || TIMESTAMP_IS_NOEND(j))

int			DecodeInterval(char **, int *, int, int *, struct tm *, fsec_t *);
int			DecodeTime(char *, int *, struct tm *, fsec_t *);
void		EncodeDateTime(struct tm *tm, fsec_t fsec, bool print_tz, int tz, const char *tzn, int style, char *str, bool EuroDates);
void		EncodeInterval(struct tm *tm, fsec_t fsec, int style, char *str);
int			tm2timestamp(struct tm *, fsec_t, int *, timestamp *);
int			DecodeUnits(int field, char *lowtoken, int *val);
bool		CheckDateTokenTables(void);
void		EncodeDateOnly(struct tm *tm, int style, char *str, bool EuroDates);
int			GetEpochTime(struct tm *);
int			ParseDateTime(char *, char *, char **, int *, int *, char **);
int			DecodeDateTime(char **, int *, int, int *, struct tm *, fsec_t *, bool);
void		j2date(int, int *, int *, int *);
void		GetCurrentDateTime(struct tm *);
int			date2j(int, int, int);
void		TrimTrailingZeros(char *);
void		dt2time(double, int *, int *, int *, fsec_t *);
int			PGTYPEStimestamp_defmt_scan(char **str, char *fmt, timestamp * d,
										int *year, int *month, int *day,
										int *hour, int *minute, int *second,
										int *tz);

extern char *pgtypes_date_weekdays_short[];
extern char *pgtypes_date_months[];
extern char *months[];
extern char *days[];
extern const int day_tab[2][13];

#endif							/* DT_H */
